/********************************************************************************
 * @file    app_tcp_server.c
 * @brief   tcp服务器应用逻辑处理
 * 
 * @author  nzttj
 * @date    2025-03-12
 * @version 1.0
 * 
 * Copyright (C) 2025 nzttj
 * 
 * 本软件使用 GNU 通用公共许可证 (GPL) 第 3 版授权。
 * 
 * 您可以自由使用、修改、分发本软件，但必须遵守以下条件：
 * 1. 源代码必须开放，且您修改后的代码也必须以 GPL 许可证发布。
 * 2. 发布或分发本软件时，必须附带本许可证，并提供源代码或获取源代码的方式。
 * 
 * 本软件按“原样”提供，不附带任何担保，使用风险由用户自行承担。
 * 
 ********************************************************************************/

#include "app_tcp_server.h"

#include <string.h>
#include <sys/param.h>
#include "lwip/sockets.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

#define MAX_CLIENTS (1)     // 限制最大并发任务数

#define FRAME_HEAD  (0x55AA)

static const char *TAG = "app_tcp_server";
static int s_client_sock = -1;
static TaskHandle_t s_server_task_handle = NULL;

/**
 * @brief 处理客户端通信的任务
 */
static void client_handler_task(void *param)
{
    s_client_sock = (int)param;
    char rx_buffer[128];

    ESP_LOGI(TAG, "Client handler task started, socket: %d", s_client_sock);

    // **开启 TCP KeepAlive**
    int keepAlive = 1;
    int keepIdle = 10;    // 空闲 10 秒后开始探测
    int keepInterval = 3; // 每 3 秒发送一次 KeepAlive 探测包
    int keepCount = 2;    // 探测 2 次失败后认为连接断开
    setsockopt(s_client_sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
    setsockopt(s_client_sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
    setsockopt(s_client_sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
    setsockopt(s_client_sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));

    for (;;)
    {
        int len = recv(s_client_sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len <= 0)
        {
            ESP_LOGI(TAG, "Client disconnected, socket: %d", s_client_sock);
            break;
        }

        rx_buffer[len > (sizeof(rx_buffer) - 1) ? (sizeof(rx_buffer) - 1) : len] = '\0';
        ESP_LOGI(TAG, "Received data: %s", rx_buffer);
        send(s_client_sock, rx_buffer, len, 0); // Echo
    }

    close(s_client_sock);
    s_client_sock = -1;
    vTaskDelete(NULL);
}

int app_tcp_server_send(const char *data, int len)
{
    if (s_client_sock < 0)
    {
        ESP_LOGE(TAG, "Socket not initialized");
        return -1;
    }

    return send(s_client_sock, data, len, 0);
}

static int tcp_log_putchar(int ch)
{
#ifdef APP_TCP_LOG
#if APP_TCP_LOG_OUTPUT
    if (s_client_sock >= 0)
    {
        send(s_client_sock, &ch, 1, 0); // 发送单个字符到 TCP
    }
#endif // APP_TCP_LOG_OUTPUT
#endif // APP_TCP_LOG
    return ch;
}

void app_tcp_log_init(void)
{
#ifdef APP_TCP_LOG
    esp_log_set_putchar(tcp_log_putchar);
#endif
}

static void _tcp_server_task(void *param)
{
    struct sockaddr_in server_addr, client_addr;
    socklen_t addr_len = sizeof(client_addr);

    // 1. Create listening socket
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        ESP_LOGE(TAG, "Failed to create socket: errno %d", errno);
        vTaskDelete(NULL);
    }

    // 2. Bind IP and port
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(APP_TCP_PORT);
    if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        ESP_LOGE(TAG, "Binding failed: errno %d", errno);
        close(listen_sock);
        vTaskDelete(NULL);
    }

    // 3. Listen on port
    if (listen(listen_sock, MAX_CLIENTS) < 0)
    {
        ESP_LOGE(TAG, "Listening failed: errno %d", errno);
        close(listen_sock);
        vTaskDelete(NULL);
    }

    ESP_LOGI(TAG, "TCP server started, listening on port: %d", APP_TCP_PORT);

    for (;;)
    {
        int client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len);
        if (client_sock < 0)
        {
            ESP_LOGE(TAG, "Accept failed: errno %d", errno);
            continue;
        }

        ESP_LOGI(TAG, "New client connected, socket: %d", client_sock);
        ESP_LOGI(TAG, "Client IP: %s, port: %d", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        // 4. Create client handler task
        if (xTaskCreate(client_handler_task, "client_handler", 2048, (void *)client_sock, 5, NULL) != pdPASS)
        {
            ESP_LOGE(TAG, "Failed to create client handler task");
            close(client_sock);
        }
    }
}

int app_tcp_server_init(void)
{
    if (s_server_task_handle != NULL)
    {
        ESP_LOGW(TAG, "TCP server already initialized");
        return 0;
    }

    return xTaskCreate(_tcp_server_task, "tcp_server", 1024, NULL, 5, &s_server_task_handle) == pdPASS ? 0 : -1;
}
